#include "GLee.h"
#include "FBO.h"
#include <exception>
#include <stdio.h>

#include "GLSupport.h"

using namespace glutil;

FBO::FBO()
{
	fb = depth_rb = color_tex = 0;
	internalformat = 0;
	target = GL_TEXTURE_2D;
	textureW = textureH = 0;
}

FBO::~FBO()
{
	release();
}

void FBO::bind()
{
	if(!GLSupport::areFBOsSupported())
		return;
	
	//glEnable(target);
	glBindTexture(target, color_tex);
}

void FBO::init(int w, int h, int format, bool mipmaps)
{
	if(!GLSupport::areFBOsSupported())
		return;
	
	// Size
	textureW = w;
	textureH = h;
	internalformat = format;

	// initalize FBO
	glGenFramebuffersEXT(1, &fb);
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb);

	// initialize color texture
	glGenTextures(1, &color_tex);
	glBindTexture(target, color_tex);
	if(mipmaps)
	{
		glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
		glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	}
	else
	{
		glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
		glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	}
	glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

	glTexImage2D(target, 0, internalformat, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
	if(mipmaps)
	{
		int temph = h, tempw = w, mips=0;;
		while(temph>2 && tempw>2)
		{
			mips++;
			temph>>=1;
			tempw>>=1;
			glTexImage2D(target, mips, internalformat, tempw, temph, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
		}
		glTexParameteri( GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE );
	}
	glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, target, color_tex, 0);

	// initialize depth renderbuffer
	glGenRenderbuffersEXT(1, &depth_rb);
	glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depth_rb);
	glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT24, w, h);
	glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, depth_rb);

	// check for errors
	GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
	switch(status)
	{
		case GL_FRAMEBUFFER_COMPLETE_EXT:
			break;
		default:
			{
				printf("Error on FBO creation. Status ID:%i\n",status);
				class FBOCreationError: public std::exception{
					virtual const char* what() const throw(){ return "An error occurred while creating an FBO";}
				};
				throw FBOCreationError();
			}
	}

	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
}

GLuint FBO::extractTexture()
{
	GLuint tex = color_tex;
	color_tex = 0;
	return tex;
}

void FBO::release()
{
	
	if (depth_rb != 0)
	{
		glDeleteRenderbuffersEXT(1, &depth_rb);
		depth_rb = 0;
	}

	if (color_tex != 0)
	{
		glDeleteTextures(1, &color_tex);
		color_tex = 0;
	}

	if (fb != 0)
	{
		glDeleteFramebuffersEXT(1, &fb);
		fb = 0;
	}
}

void FBO::beginCapture()
{
    printf("\n\nCurrent error int FBO::beginCapture: %i\n\n",glGetError());
    if(GLSupport::areFBOsSupported())
		glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb);
}

void FBO::endCapture()
{
	if(GLSupport::areFBOsSupported())
		glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
}

void FBO::clear()
{
	if(!GLSupport::areFBOsSupported())
		return;
	beginCapture();
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	endCapture();
}
